package com.fit.exercise.msgp2p;

import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

public class UDPUtil {
	public static void sendMessage(String message, PersonInfo person,
			byte type, byte flag, byte[] optionData) throws IOException {
		sendMessage(message.getBytes(), person, type, flag, optionData);
	}

	public static void sendMessage(byte[] message, PersonInfo person,
			byte type, byte flag, byte[] optionData) throws IOException {
		DatagramSocket socket = null;
		try {
			InetAddress address = InetAddress.getByName(person.getIpAddress());

			Packet sendData = new Packet.Builder(type, flag,
					T2Messenger.myInfo.getPort(), message).optionData(
					optionData).build();

			DatagramPacket packet = new DatagramPacket(sendData.getBytes(), 0,
					sendData.getPacketLength(), address, person.getPort());

			socket = new DatagramSocket();
			socket.send(packet);
		} catch (IOException e) {
			throw e;
		} finally {
			if (socket != null)
				socket.close();
		}
	}

	// Listener for multiple chat frame
	public static void listenToReceiveMessage() {
		Thread listenThread = new Thread(new Runnable() {

			@Override
			public void run() {
				DatagramSocket socket = null;

				try {
					socket = new DatagramSocket(T2Messenger.myInfo.getPort());

					// Set timeout socket for listen change port event
					socket.setSoTimeout(400);
					while (true) {
						DatagramPacket packet = new DatagramPacket(
								new byte[512], 512);
						// Change port
						if (!T2Messenger.isListen) {
							if (socket != null) {
								socket.close();
							}
							socket = new DatagramSocket(T2Messenger.myInfo
									.getPort());
						}

						try {
							socket.receive(packet);
						} catch (SocketTimeoutException e) {
							// System.out.println(e);
							continue;
						}

						Packet receivedData = new Packet(packet.getData());

						if (receivedData.getFlag() == Packet.FLAG_DATA) {
							// DATA FLAG
							switch (receivedData.getType()) {
							case Packet.TYPE_ADD_FRIEND_MSG:
								processAddFriendMSG(receivedData);
								break;
							case Packet.TYPE_CHAT_MSG:
								processReceiveChatMSG(packet, receivedData);
								break;
							case Packet.TYPE_GROUP_CHAT_REQ: // Group chat
																// request
								processReceiveReqChatGroup(packet, receivedData);
								break;
							case Packet.TYPE_GROUP_CHAT_RES: // Group chat
																// response
								processReceiveResChatGroup(packet, receivedData);
								break;
							case Packet.TYPE_GROUP_CHAT_MSG:
								processReceiveGroupChatMSG(packet, receivedData);
								break;
							case Packet.TYPE_GROUP_CHAT_ADD_MORE:
								processReceiveReqAddMoreFriendToChatGroup(
										packet, receivedData);
								break;
							default:
								break;
							}
						} else {
							// ACK FLAG
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					if (socket != null)
						socket.close();
				}
			}

			private void processReceiveGroupChatMSG(DatagramPacket packet,
					Packet receivedData) throws BadLocationException {

				PersonInfo sender = Util.findByIpAndPort(packet.getAddress()
						.getHostName(), receivedData.getListenPort(),
						T2Messenger.friendList);

				GroupChatFrame chatedFrame = new GroupChatFrame();
				chatedFrame.setChatGroupNumber(ByteBuffer.wrap(
						receivedData.getOptionData()).getLong());

				int idx = T2Messenger.groupChatFrames.indexOf(chatedFrame);
				if (idx < 0) {
					T2Messenger.groupChatFrames.add(chatedFrame);
				} else {
					chatedFrame = T2Messenger.groupChatFrames.get(idx);
				}
				chatedFrame.setVisible(true);

				// Prepare for text panel
				StyledDocument doc = chatedFrame.getChatContent()
						.getStyledDocument();

				SimpleAttributeSet senderKeyword = new SimpleAttributeSet();
				StyleConstants.setBold(senderKeyword, true);
				StyleConstants.setForeground(senderKeyword, Color.GREEN);

				doc.insertString(doc.getLength(), sender.getName() + " say: ",
						senderKeyword);
				doc.insertString(doc.getLength(),
						new String(receivedData.getData()) + "\n", null);

			}

			private void processReceiveReqChatGroup(DatagramPacket packet,
					Packet receivedData) throws ClassNotFoundException,
					IOException {

				PersonInfo sender = Util.findByIpAndPort(packet.getAddress()
						.getHostName(), receivedData.getListenPort(),
						T2Messenger.friendList);
				if (JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(
						null,
						sender.getName()
								+ " invite you to join the chat group. Do you want to join?",
						"Join chat group", JOptionPane.OK_CANCEL_OPTION)) {
					GroupChatFrame groupChatFrame = new GroupChatFrame();

					Long chatGroupNumber = ByteBuffer.wrap(
							receivedData.getOptionData()).getLong();
					groupChatFrame.setChatGroupNumber(chatGroupNumber);
					groupChatFrame.addElementToListModel(sender);
					groupChatFrame.setVisible(true);
					T2Messenger.groupChatFrames.add(groupChatFrame);

					List<PersonInfo> invitedList = Util
							.convertFromByteArray(receivedData.getData());
					// Remove mine
					invitedList.remove(T2Messenger.myInfo);

					for (PersonInfo personInfo : invitedList) {
						sendMessage("", personInfo, Packet.TYPE_GROUP_CHAT_RES,
								Packet.FLAG_DATA,
								ByteBuffer.allocate(Packet.OPTION_DATA_LENGTH)
										.putLong(chatGroupNumber).array());
					}

				}
			}

			private void processReceiveReqAddMoreFriendToChatGroup(
					DatagramPacket packet, Packet receivedData)
					throws ClassNotFoundException, IOException {

				PersonInfo sender = Util.findByIpAndPort(packet.getAddress()
						.getHostName(), receivedData.getListenPort(),
						T2Messenger.friendList);
				if (JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(
						null,
						sender.getName()
								+ " invite you to join the chat group. Do you want to join?",
						"Join chat group", JOptionPane.OK_CANCEL_OPTION)) {
					GroupChatFrame groupChatFrame = new GroupChatFrame();

					Long chatGroupNumber = ByteBuffer.wrap(
							receivedData.getOptionData()).getLong();
					groupChatFrame.setChatGroupNumber(chatGroupNumber);
					groupChatFrame.setVisible(true);
					T2Messenger.groupChatFrames.add(groupChatFrame);

					List<PersonInfo> invitedList = Util
							.convertFromByteArray(receivedData.getData());
					// Remove mine
					invitedList.remove(T2Messenger.myInfo);

					int idx = invitedList.indexOf(sender);

					for (int i = 0; i < invitedList.size(); i++) {
						// Add all exist friend in group chat
						if (i <= idx) {
							groupChatFrame.addElementToListModel(invitedList
									.get(i));
						}

						// Send ACCEPT response
						sendMessage("", invitedList.get(i),
								Packet.TYPE_GROUP_CHAT_RES, Packet.FLAG_DATA,
								ByteBuffer.allocate(Packet.OPTION_DATA_LENGTH)
										.putLong(chatGroupNumber).array());
					}

				}
			}

			private void processReceiveResChatGroup(DatagramPacket packet,
					Packet receivedData) throws ClassNotFoundException,
					IOException {

				PersonInfo sender = Util.findByIpAndPort(packet.getAddress()
						.getHostName(), receivedData.getListenPort(),
						T2Messenger.friendList);

				GroupChatFrame groupChatFrame = new GroupChatFrame();
				Long chatGroupNumber = ByteBuffer.wrap(
						receivedData.getOptionData()).getLong();
				groupChatFrame.setChatGroupNumber(chatGroupNumber);

				int idx = T2Messenger.groupChatFrames.indexOf(groupChatFrame);
				if (idx > -1) {
					groupChatFrame = T2Messenger.groupChatFrames.get(idx);
				} else {
					T2Messenger.groupChatFrames.add(groupChatFrame);
					groupChatFrame.setVisible(true);
				}
				groupChatFrame.addElementToListModel(sender);
			}

			private void processReceiveChatMSG(DatagramPacket packet,
					Packet receivedData) throws BadLocationException {
				String senderIpAddress = packet.getAddress().getHostName();
				Integer senderListenPort = receivedData.getListenPort();

				PersonInfo person = Util.findByIpAndPort(senderIpAddress,
						senderListenPort, T2Messenger.friendList);

				ChatFrame chatedFrame = new ChatFrame();
				chatedFrame.setChatFriend(person);

				int idx = T2Messenger.chatFrames.indexOf(chatedFrame);
				if (idx < 0) {
					T2Messenger.chatFrames.add(chatedFrame);
				} else {
					chatedFrame = T2Messenger.chatFrames.get(idx);
				}
				chatedFrame.setVisible(true);

				// Prepare for text panel
				StyledDocument doc = chatedFrame.getChatContent()
						.getStyledDocument();

				SimpleAttributeSet senderKeyword = new SimpleAttributeSet();
				StyleConstants.setBold(senderKeyword, true);
				StyleConstants.setForeground(senderKeyword, Color.GREEN);

				doc.insertString(doc.getLength(), chatedFrame.getChatFriend()
						.getName() + " say: ", senderKeyword);
				doc.insertString(doc.getLength(),
						new String(receivedData.getData()) + "\n", null);
			}

			private void processAddFriendMSG(Packet receivedData)
					throws IOException, ClassNotFoundException {
				byte[] tmp = receivedData.getData();
				ObjectInputStream ois = new ObjectInputStream(
						new ByteArrayInputStream(tmp));
				PersonInfo o = (PersonInfo) ois.readObject();

				String msg = o.getName() + "\n" + o.getIpAddress() + "\n"
						+ o.getPort();

				if (JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(
						null, msg, "New friend", JOptionPane.OK_CANCEL_OPTION)) {
					String strName = JOptionPane
							.showInputDialog("Input new name");
					if (strName != null && !strName.isEmpty()) {
						o.setName(strName);
					}

					Util.saveToFile(Util.filePath, o);
					T2Messenger.listModel.addElement(o.getName() + " - ("
							+ o.getIpAddress() + ":" + o.getPort() + ")");
					T2Messenger.friendList.add(o);
				}
			}
		});

		listenThread.start();
	}
}
